<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>三维向量及控制物体前进</title>
<style>
html,body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}
</style>
</head>

<body>
<script type="importmap">
{
    "imports": {
        "three": "../js/threejs/three.module.js",
        "three/addons/": "../js/threejs/jsm/"
    }
}
</script>
<script type="module">
    import * as THREE from 'three';
    import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
    import { DragControls } from 'three/addons/controls/DragControls.js';
    import { TransformControls } from 'three/addons/controls/TransformControls.js';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';//倒入轨道控制器
    import {RenderPass} from 'three/addons/postprocessing/RenderPass.js';
    import {OutlinePass} from 'three/addons/postprocessing/OutlinePass.js';
    import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js';
    import * as ThreeComm from '../js/three-comm.js';
    

    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机
    const camera = new THREE.PerspectiveCamera( // 透视相机
        75, // 视角 角度数
        window.innerWidth / window.innerHeight, // 宽高比 占据屏幕
        0.1, // 近平面（相机最近能看到物体）
        1000, // 远平面（相机最远能看到物体）
    );
    camera.position.set(0, 10, 30);
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer({
        antialias: true, // 抗锯齿
    });

    // 设置渲染器宽高
    renderer.setSize(window.innerWidth, window.innerHeight);
    // renderer（渲染器）的dom元素添加到我们的HTML文档中
    document.body.appendChild(renderer.domElement);

    // 地面
    const plane = new THREE.Mesh(
        new THREE.PlaneGeometry(120, 100),
        new THREE.MeshStandardMaterial({
            color: 0x817936,
        })
    );
    plane.rotation.x = -Math.PI / 2;
    // 添加到场景中
    scene.add(plane);

    // 添加灯光
    const ambientLight = new THREE.AmbientLight(0x404040, 100);
    scene.add(ambientLight);

    // 创建球体
    const sphereGeometry = new THREE.SphereGeometry(2, 32, 32);
    const sphereMaterial = new THREE.MeshStandardMaterial({
        color: 0xfff000,
    })
    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.set(0, 2, 0);
    scene.add(sphere);

    //创建一个立方体几何对象Geometry
    var geometry = new THREE.BoxGeometry(6, 6, 6);
    //给几何体创建材质，这里是改为蓝色，材质对象Material
    var material = new THREE.MeshLambertMaterial({color: 0xFF00ff});
    //网格模型对象Mesh
    var mesh = new THREE.Mesh(geometry, material);
    //网格模型添加到场景中，每个模型最终都要添加到场景中才会被渲染
    mesh.name = 'box'
    mesh.position.x = 10;
    mesh.position.y = 10;
    mesh.position.z = 0;

    scene.add(mesh);

    

    const axesHelper = new THREE.AxesHelper(25);
    scene.add(axesHelper);
    // 键位
    const keyEvent = {
        W: false,
    };

    // 事件
    window.addEventListener("keydown", (event) => {
        const keyCode = event.key.toUpperCase();
        console.log(keyCode);
        if (keyEvent.hasOwnProperty(keyCode)) {
            keyEvent[keyCode] = true;
        }
    });
    window.addEventListener("keyup", (event) => {
        const keyCode = event.key.toUpperCase();
        if (keyEvent.hasOwnProperty(keyCode)) {
            keyEvent[keyCode] = false;
        }
    });
    const velocity = new THREE.Vector3(0,0,-0.033333);

    // 控制器
    const control = new OrbitControls(camera, renderer.domElement);
    // 开启阻尼惯性，默认值为0.05
    control.enableDamping = true;


    var arrow //= createArraw(mesh,0)

    arrow = createArraw(mesh,0)
    arrow = createArraw(mesh,1)
    arrow = createArraw(mesh,2)
    arrow = createArraw(mesh,3)
    arrow = createArraw(mesh,4)
    arrow = createArraw(mesh,5)
    

    function createArraw(hMess,faceIdx){
      if(ThreeComm.isEmpty(hMess)){
        console.log('hMess is null');
        return
      }
      if(!ThreeComm.isNum(faceIdx)){
        console.log('faceIdx is not num');
        return
      }
      var points     = ThreeComm.getMessPoints(hMess);
      var facePoints = ThreeComm.getMessFacePoints(hMess,faceIdx,points);
      var centerPoint= ThreeComm.getCenterPoint(facePoints);
      

      //已知三角形三个顶点的坐标，计算三角形法线方向
      var idx1 = 0;
      var idx2 = 1;
      var idx3 = 2;

      if(faceIdx==0){
        idx1 = 2;
        idx2 = 1;
        idx3 = 0;
      }

      var p1 = ThreeComm.pointToVector3(facePoints[idx1]);
      var p2 = ThreeComm.pointToVector3(facePoints[idx2]);
      var p3 = ThreeComm.pointToVector3(facePoints[idx3]);

      // 三个顶点构建两个向量，按照三角形顶点的顺序，构建1指向2的向量，2指向3的向量
      var a = p2.clone().sub(p1);
      var b = p3.clone().sub(p2);
      var c = a.clone().cross(b);
      c.normalize();//向量c归一化表示三角形法线方向

      
      // 可视化向量a和b叉乘结果：向量c
      var arrow = new THREE.ArrowHelper(centerPoint,c,  8, 0xfff000);
      arrow.userData = {type:'drag',faceIdx:faceIdx}
      //console.log('points=',points,'facePoints=',facePoints,'centerPoint=',centerPoint,'c=',c,'arrow=',arrow);
      hMess.add(arrow);
    }
    

    //创建法向量的箭头 https://blog.csdn.net/u014291990/article/details/135322526
    function createArrawEx(hMess){
      if(ThreeComm.isEmpty(hMess)){
        return
      }
      
      //已知三角形三个顶点的坐标，计算三角形法线方向
      const p1 = new THREE.Vector3(0, 0, 0);
      const p2 = new THREE.Vector3(5, 0, 0);
      const p3 = new THREE.Vector3(0, 10, 0);
      // 三个顶点构建两个向量，按照三角形顶点的顺序，构建1指向2的向量，2指向3的向量
      const a = p2.clone().sub(p1);
      const b = p3.clone().sub(p2);

      const c = a.clone().cross(b);
      c.normalize();//向量c归一化表示三角形法线方向
      // 可视化向量a和b叉乘结果：向量c
      const arrow = new THREE.ArrowHelper(c, p3, 0.05, 0xff0000);
      
      hMess.add(arrow);
    }

    // 渲染循环动画
    function animate() {
        // 在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环（在大多数屏幕上，刷新率一般是60次/秒）
        requestAnimationFrame(animate);
        if (keyEvent.W) {
            sphere.position.add(velocity);
        }
        // 更新控制器。如果没在动画里加上，那必须在摄像机的变换发生任何手动改变后调用
        control.update();
        renderer.render(scene, camera);
    };

    // 执行动画
    animate();
</script>
</body>
</html>